package org.cainiao.oauth2.client.core.component;

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import java.io.IOException;

/**
 * 发起登录，逻辑与 LoginUrlAuthenticationEntryPoint 相同<br />
 * 专门用于 AJAX 请求的重定向<br />
 * AJAX 请求的 302 重定向无法正确执行，因此改为返回 401 错误码，并携带重定向 URL<br />
 * 由前端 JS 负责在 window 上重定向<br />
 * 由于无法设置 LoginUrlAuthenticationEntryPoint 的 RedirectStrategy，因此只能对其进行扩展<br />
 *
 * <p>
 * Author: Cai Niao(wdhlzd@163.com)<br />
 */
public class AjaxLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {

    private static final Log LOGGER = LogFactory.getLog(AjaxLoginUrlAuthenticationEntryPoint.class);

    private final RedirectStrategy redirectStrategy = getRedirectStrategy();

    private RedirectStrategy getRedirectStrategy() {
        DefaultRedirectStrategy defaultRedirectStrategy = new DefaultRedirectStrategy();
        defaultRedirectStrategy.setStatusCode(HttpStatus.UNAUTHORIZED);
        return defaultRedirectStrategy;
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {
        if (!this.isUseForward()) {
            // redirect to login page. Use https if forceHttps true
            String redirectUrl = buildRedirectUrlToLoginPage(request, response, authException);
            redirectStrategy.sendRedirect(request, response, redirectUrl);
            return;
        }
        String redirectUrl = null;
        if (isForceHttps() && "http".equals(request.getScheme())) {
            // First redirect the current request to HTTPS. When that request is received,
            // the forward to the login page will be used.
            redirectUrl = buildHttpsRedirectUrlForRequest(request);
        }
        if (redirectUrl != null) {
            redirectStrategy.sendRedirect(request, response, redirectUrl);
            return;
        }
        String loginForm = determineUrlToUseForThisRequest(request, response, authException);
        LOGGER.debug(LogMessage.format("Server side forward to: %s", loginForm));
        RequestDispatcher dispatcher = request.getRequestDispatcher(loginForm);
        dispatcher.forward(request, response);
    }

    /**
     * @param loginFormUrl URL where the login page can be found. Should either be
     *                     relative to the web-app context path (include a leading {@code /}) or an absolute
     *                     URL.
     */
    public AjaxLoginUrlAuthenticationEntryPoint(String loginFormUrl) {
        super(loginFormUrl);
    }
}
